package cn.quang.search.admin.service.impl;

import cn.quang.search.admin.entity.dto.Operator;
import cn.quang.search.admin.entity.dto.Result;
import cn.quang.search.admin.entity.po.EntityFieldDic;
import cn.quang.search.admin.entity.po.SearchSceneContextConfig;
import cn.quang.search.admin.entity.po.SearchScenePresetConditionConfig;
import cn.quang.search.admin.entity.vo.scene.SearchSceneContextRequest;
import cn.quang.search.admin.entity.vo.scenecontext.SearchSceneContextVO;
import cn.quang.search.admin.enums.PresetConditionValueTypeEnum;
import cn.quang.search.admin.mapper.EntityFieldDicMapper;
import cn.quang.search.admin.mapper.SearchSceneContextConfigMapper;
import cn.quang.search.admin.mapper.SearchScenePresetConditionConfigMapper;
import cn.quang.search.admin.service.ISearchSceneContextConfigService;
import cn.quang.search.admin.service.common.RedisService;
import cn.quang.search.admin.utils.AssertUtils;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @author:  Entity Generator
 * @since:  2024-03-26 15:24:47
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SearchSceneContextConfigService implements ISearchSceneContextConfigService {

	private final Logger logger = LoggerFactory.getLogger(SearchSceneContextConfigService.class);

	private final SearchSceneContextConfigMapper searchSceneContextConfigMapper;
	private final SearchScenePresetConditionConfigMapper searchScenePresetConditionConfigMapper;
	private final EntityFieldDicMapper entityFieldDicMapper;
	
	private final SearchConfigChangeService searchConfigChangeService;
	private final RedisService redisService;
	private static final String LOCK_KEY = "CRM3$SEARCH_SCENE_CONTEXT_DIC$";
	private static final int EXPIRE_SECONDS = 60;

	@Override
	public Result<?> addOrUpdate(Operator operator, SearchSceneContextRequest request){
		Result<?> aopResult = new Result<>();

		long sceneId = request.getSceneId();
		AssertUtils.argumentNegativeOrZero(sceneId, "必须提供场景ID");
		List<SearchSceneContextRequest.ContextRequest> contextRequests = request.getContexts();
		if(CollectionUtils.isEmpty(contextRequests)) {
			contextRequests = Lists.newArrayList();
		}

		for (SearchSceneContextRequest.ContextRequest contextRequest : contextRequests) {
			String dic = contextRequest.getDic();
			AssertUtils.argumentBlank(dic, "必须提供Dic字段");
			String name = contextRequest.getName();
			AssertUtils.argumentBlank(name, "必须提供名称");
			int fieldType = contextRequest.getFieldType();
			AssertUtils.argumentNegativeOrZero(fieldType, "必须提供类型");
		}

		Date now = new Date();

		// 存储增删改的表名称，用于输出日志
		List<String> newContextDices = Lists.newArrayList();
		List<String> modifyContextDices = Lists.newArrayList();
		List<String> deleteContextDices = Lists.newArrayList();

		String uniqueKey = LOCK_KEY + operator.getOperatorId() + "$" + sceneId;
		String requestId = UUID.randomUUID().toString();
		if(redisService.tryLock(uniqueKey, requestId, EXPIRE_SECONDS)) {
			try {
				List<SearchSceneContextConfig> contextConfigs = searchSceneContextConfigMapper.selectBySceneId(sceneId);
				if(contextConfigs == null) { contextConfigs = Lists.newArrayList(); }
				Map<String, SearchSceneContextConfig> contextConfigMap = contextConfigs.stream().collect(Collectors.toMap(SearchSceneContextConfig::getDic, v -> v));
				Map<String, SearchSceneContextRequest.ContextRequest> requestContextMap = contextRequests.stream().collect(Collectors.toMap(SearchSceneContextRequest.ContextRequest::getDic, v -> v));

				// 执行删除(解除自定义字典和逻辑表的关联关系，需要校验是否存在某个场景基于该逻辑表关联了该自定义字典，如果存在，则应该先解除场景下的关联关系)
				List<Long> deleteIds = Lists.newArrayList();
				List<SearchScenePresetConditionConfig> presetConditions = searchScenePresetConditionConfigMapper.selectBySceneId(sceneId);
				Map<Long, SearchScenePresetConditionConfig> contextPresetConditionMap = presetConditions.stream().filter(input -> input.getType() == PresetConditionValueTypeEnum.CONTEXT.getCode()).collect(Collectors.toMap(k -> Long.parseLong(k.getValue()), v -> v, (a, b) -> a));
				for (SearchSceneContextConfig contextConfig : contextConfigs) {
					String dic = contextConfig.getDic();
					if(!requestContextMap.containsKey(dic)){
						// 删除，需要检查场景中是否有预设查询条件依赖了该值
						if(contextPresetConditionMap.containsKey(contextConfig.getId())) {
							SearchScenePresetConditionConfig searchScenePresetConditionConfig = contextPresetConditionMap.get(contextConfig.getId());
							long fieldDicId = searchScenePresetConditionConfig.getFieldDicId();
							EntityFieldDic fieldDic = entityFieldDicMapper.selectById(fieldDicId);
							return aopResult.changeErr(String.format("该上下文已被预设查询条件%s(%s)关联，请先取消关联后再删除", String.format("%s-%s", fieldDic.getDic(), fieldDic.getName()), searchScenePresetConditionConfig.getOperator()));
						}
						deleteIds.add(contextConfig.getId());
						deleteContextDices.add(contextConfig.getDic());
					}
				}
				for (Long id : deleteIds) { searchSceneContextConfigMapper.deleteById(id); }

				// 比较更新 ： 存在的更新，不存在的新增，未出现在请求中的删除
				for (SearchSceneContextRequest.ContextRequest contextRequest : contextRequests) {
					SearchSceneContextConfig contextConfig = contextConfigMap.get(contextRequest.getDic());
					if(contextConfig != null){
						// 已存在的自定义字典，执行更新
						contextConfig.setDic(contextRequest.getDic());
						contextConfig.setName(contextRequest.getName());
						contextConfig.setFieldType(contextRequest.getFieldType());
						contextConfig.setAssociateRequestParam(contextRequest.getAssociateRequestParam());
						contextConfig.setNotNull(contextRequest.isNotNull());
						contextConfig.setModifierId(operator.getOperatorId());
						contextConfig.setModifyTime(now);
						searchSceneContextConfigMapper.updateById(contextConfig);
						modifyContextDices.add(contextConfig.getDic());
					} else {
						// 新增
						contextConfig = new SearchSceneContextConfig();
						contextConfig.setSceneId(sceneId);
						contextConfig.setDic(contextRequest.getDic());
						contextConfig.setName(contextRequest.getName());
						contextConfig.setFieldType(contextRequest.getFieldType());
						contextConfig.setAssociateRequestParam(contextRequest.getAssociateRequestParam());
						contextConfig.setNotNull(contextRequest.isNotNull());
						contextConfig.setCreatorId(operator.getOperatorId());
						contextConfig.setCreateTime(now);
						contextConfig.setModifierId(operator.getOperatorId());
						contextConfig.setModifyTime(now);
						searchSceneContextConfigMapper.insert(contextConfig);
						newContextDices.add(contextRequest.getDic());
					}
				}
				searchConfigChangeService.triggerBySceneId(sceneId);
			} catch (Exception e) {
				logger.error(String.format("场景(%s)的上下文配置操作失败", sceneId), e);
				return aopResult.changeErr("场景的上下文配置操作失败: " + e.getMessage());
			} finally {
				redisService.releaseLock(uniqueKey, requestId);
			}
		} else {
			return aopResult.changeErr("请不要频繁操作！");
		}

		logger.info(String.format("场景新增上下文%s，修改上下文%s，删除上下文%s", newContextDices, modifyContextDices, deleteContextDices));
		return aopResult;
	}

	@Override
	public Result<List<SearchSceneContextVO>> contexts4View(long sceneId) {
		Result<List<SearchSceneContextVO>> aopResult = new Result<>();

		List<SearchSceneContextVO> vos = Lists.newArrayList();
		try {
			List<SearchSceneContextConfig> searchSceneContextConfigs = searchSceneContextConfigMapper.selectBySceneId(sceneId);
			if(CollectionUtils.isNotEmpty(searchSceneContextConfigs)) {
				for (SearchSceneContextConfig searchSceneContextConfig : searchSceneContextConfigs) {
					SearchSceneContextVO vo = new SearchSceneContextVO();
					vo.setDic(searchSceneContextConfig.getDic());
					vo.setName(searchSceneContextConfig.getName());
					vo.setFieldType(searchSceneContextConfig.getFieldType());
					vo.setAssociateRequestParam(searchSceneContextConfig.getAssociateRequestParam());
					vo.setNotNull(searchSceneContextConfig.getNotNull());
					vos.add(vo);
				}
			}
		} catch (Exception e) {
			logger.error(String.format("获取场景(sceneId=%s)的上下文信息失败", sceneId), e);
			return aopResult.changeErr("系统异常");
		}

		return aopResult.changeSucc(vos);
	}
}
